package com.niiwoo.civet.trade.service.local.newRefund.callback;


import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.account.dto.request.ReceivedRepaymentSuccessAccountingRequestDTO;
import com.niiwoo.civet.account.enums.OrderTypeEnum;
import com.niiwoo.civet.account.service.trade.TransOrderDubboService;
import com.niiwoo.civet.base.dto.BaseFeeDetailDTO;
import com.niiwoo.civet.base.enums.BorrowProjectTypeEnum;
import com.niiwoo.civet.base.enums.FeeTypeEnum;
import com.niiwoo.civet.base.enums.OrderRefundTypeEnum;
import com.niiwoo.civet.base.enums.RefundTypeEnum;
import com.niiwoo.civet.trade.constant.enums.ReceivedStatusEnum;
import com.niiwoo.civet.trade.dao.entity.*;
import com.niiwoo.civet.trade.dao.mapper.*;
import com.niiwoo.civet.trade.dto.common.BathTransactionNotifyDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.service.local.newRefund.common.RefundCommonService;
import com.niiwoo.civet.trade.service.local.newRefund.refundDeal.core.RefundDetailHandlerService;
import com.niiwoo.tripod.lanmao.component.LanMaoDirectService;
import com.niiwoo.tripod.lanmao.enums.BusinessTypeEnum;
import com.niiwoo.tripod.lanmao.enums.ResponseCodeEnum;
import com.niiwoo.tripod.lanmao.enums.TradeQueryTypeEnum;
import com.niiwoo.tripod.lanmao.enums.TransationStatusEnum;
import com.niiwoo.tripod.lanmao.request.QueryTransactionRequest;
import com.niiwoo.tripod.lanmao.response.QueryTransactionResponse;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;
import static org.springframework.transaction.annotation.Propagation.REQUIRES_NEW;


/**
 * 还款银行存管回调处理(投资人)
 *
 * @author liaojunjun
 */
@Service
public class RefundBankBackLocalService {

    private static final Logger logger = LoggerFactory.getLogger(RefundBankBackLocalService.class);

    @Autowired
    private RefundOrderMapperExt refundOrderMapperExt;

    @Autowired
    private RefundOrderDetailMapperExt refundOrderDetailMapperExt;

    @Autowired
    private RefundOrderDetailBizMapperExt refundOrderDetailBizMapperExt;

    @Autowired
    private ProjectInvestorPlanMapperExt projectInvestorPlanMapperExt;

    @Autowired
    private ProjectGuaranteePlanMapperExt projectGuaranteePlanMapperExt;

    @Autowired
    private ProjectMapperExt projectMapperExt;

    @Reference(version = "1.0.0")
    private TransOrderDubboService transOrderDubboService;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private LanMaoDirectService lanMaoDirectService;

    @Autowired
    private RefundDetailHandlerService refundDetailHandlerService;

    @Autowired
    private ProjectInvestOrderMapperExt projectInvestOrderMapperExt;

    @Autowired
    private InvestorCreditorMapperExt investorCreditorMapperExt;

    @Autowired
    private ProjectGuaranteeServicePlanMapper projectGuaranteeServicePlanMapper;

    @Autowired
    private ProjectGuaranteeServicePlanMapperExt projectGuaranteeServicePlanMapperExt;

    @Autowired
    private InvestorReceiveRecordMapperExt investorReceiveRecordMapperExt;

    @Autowired
    private RefundCommonService refundCommonService;
    @Autowired
    private ProjectPackageInvestOrderDetailMapperExt projectPackageInvestOrderDetailMapperExt;
    @Autowired
    private ProjectPackageInvestOrderMapperExt projectPackageInvestOrderMapperExt;


    /**
     * 存管银行还款结果通知
     *
     * @param bankRefundNotifyDTOList
     */
    public void bankRefundResultNotify(List<BathTransactionNotifyDTO> bankRefundNotifyDTOList) {
        Assert.notNull(bankRefundNotifyDTOList, "bankRefundResultNotify bankRefundNotifyDTOList is null.");
        for (BathTransactionNotifyDTO bathTransactionNotifyDTO : bankRefundNotifyDTOList) {
            Assert.notNull(bathTransactionNotifyDTO, "bankRefundResultNotify bathTransactionNotifyDTO is null.");
            logger.info("bankRefundResultNotify detailRequestNo:{}", bathTransactionNotifyDTO.getAsyncRequestNo());
            try {
                ((RefundBankBackLocalService) AopContext.currentProxy()).bankRefundResultNotify(bathTransactionNotifyDTO);
            } catch (Exception e){
                logger.error("bankRefundResultNotify Exception detailRequestNo:{}", bathTransactionNotifyDTO.getAsyncRequestNo(), e);
            }
        }
    }

    @Transactional
    public void bankRefundResultNotify(BathTransactionNotifyDTO bankRefundNotifyDTO) {
        RefundOrderDetail refundOrderDetail = refundOrderDetailMapperExt.queryRefundOrderDetailByDetailRequestNoForUpdate(bankRefundNotifyDTO.getAsyncRequestNo());
        Assert.notNull(refundOrderDetail, "doLendResultCallBack lendResultCallBack is null, detailRequestNo=" + bankRefundNotifyDTO.getAsyncRequestNo());
        if (!refundOrderDetail.getStatus().equals(RefundOrderDetailStatusEnum.HANDLING.getValue())) {
            // 订单状态已经被修改,此订单已经被处理过 后续操作中断
            logger.info("更新还款/垫付订单明细表状态 订单状态已经被修改,此订单已经被处理过 后续操作中断 projectId:{}, detailRequestNo:{}, status:{}", refundOrderDetail.getProjectId(), bankRefundNotifyDTO.getAsyncRequestNo(), refundOrderDetail.getStatus());
            return;
        }
        logger.info("更新还款/垫付订单明细表状态 projectId:{}, detailRequestNo:{}, status:{}", refundOrderDetail.getProjectId(), bankRefundNotifyDTO.getAsyncRequestNo(), bankRefundNotifyDTO.getStatus());

        // 更新还款/垫付订单状态
        RefundOrderDetail updateRecord = new RefundOrderDetail();
        updateRecord.setId(refundOrderDetail.getId());
        updateRecord.setStatus(LendOrderStatusEnum.valueOf(bankRefundNotifyDTO.getStatus()).getValue());
        updateRecord.setTransactionTime(new Date());

        if (RefundOrderDetailStatusEnum.FAIL.name().equals(bankRefundNotifyDTO.getStatus())) {
            // 还款/垫付明细失败 部分失败或全部失败都使用JOB处理，通知暂不处理，可考虑调用告警邮件
            logger.warn("银行存管返回状态为处理失败 bankRefundNotifyDTOList: {}", JSON.toJSON(bankRefundNotifyDTO));

            updateRecord.setErrorCode(bankRefundNotifyDTO.getErrorCode());
            updateRecord.setErrorMessage(bankRefundNotifyDTO.getErrorMessage());
            refundOrderDetailMapperExt.updateByPrimaryKeySelective(updateRecord);

            RefundOrder refundOrder = refundOrderMapperExt.selectByPrimaryKey(refundOrderDetail.getRefundOrderId());

            // 发送还款订单明细处理完成消息
            try {
                refundDetailHandlerService.refundDetailHandlerMQ(refundOrder.getId());
            } catch (Throwable e) {
                logger.error("发送还款订单明细处理完成消息异常 projectId:{}, batchNo:{},", refundOrder.getProjectId(), refundOrder.getBatchNo(), e);
            }
        } else if (RefundOrderDetailStatusEnum.SUCCESS.name().equals(bankRefundNotifyDTO.getStatus())) {
            updateRecord.setAccountingStatus(RefundOrderAccountingStatusEnum.HANDLING.getValue());
            refundOrderDetailMapperExt.updateByPrimaryKeySelective(updateRecord);

            RefundOrder refundOrder = refundOrderMapperExt.selectByPrimaryKey(refundOrderDetail.getRefundOrderId());

            List<RefundOrderDetailBiz> refundOrderDetailBizList = refundOrderDetailBizMapperExt.queryRefundOrderDetailBizByDetailRequestNo(refundOrderDetail.getDetailRequestNo());
            for (RefundOrderDetailBiz refundOrderDetailBiz : refundOrderDetailBizList) {
                if (refundOrderDetailBiz.getAmount().compareTo(BigDecimal.ZERO) == 0) {
                    // 无效业务明细数据
                    continue;
                }
                // 有效业务明细数据

                // 1.借款人还投资人资金：更新投资人回款计划(投资订单回款状态)
                // 2.垫付：更新投资人回款计划(投资订单回款状态)、创建担保公司回款计划
                // 3.借款人换代偿款:更新担保公司回款计划
                ProjectInvestorPlan projectInvestorPlan = projectInvestorPlanMapperExt.selectByPrimaryKey(refundOrderDetailBiz.getBackPlanId());
                Project project = projectMapperExt.selectByPrimaryKey(refundOrderDetail.getProjectId());
                String bizType = refundOrderDetailBiz.getBizType();
                if (BusinessTypeEnum.REPAYMENT.name().equals(bizType)) {
                    updateInvestorPlanAndInvestOrderEvolutionStatus(projectInvestorPlan, refundOrderDetailBiz, refundOrder.getCurrentDate(),refundOrder.getRefundType());
                } else if (BusinessTypeEnum.COMPENSATORY.name().equals(bizType)) {
                    updateInvestorPlanAndInvestOrderEvolutionStatus(projectInvestorPlan, refundOrderDetailBiz, refundOrder.getCurrentDate(),refundOrder.getRefundType());
                    buildProjectGuaranteePlan(refundOrderDetailBiz, projectInvestorPlan);
                } else if (BusinessTypeEnum.COMPENSATORY_REPAYMENT.name().equals(bizType)) {
                    updateGuaranteePlan(refundOrderDetailBiz, refundOrder.getCurrentDate());
                } else if(BusinessTypeEnum.PROFIT.name().equals(bizType) || BusinessTypeEnum.COMMISSION.name().equals(bizType)){
                    // 代还分润 担保服务费|咨询服务费 代还分佣 平台管理费
                    // 垫付分润 担保服务费|咨询服务费 垫付分佣 平台管理费
                    if(RefundTradeTypeEnum.REPLACE.getTradeType().equals(refundOrderDetailBiz.getTradeType())
                            || RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderDetailBiz.getTradeType())){
                        buildProjectGuaranteeServicePlan(refundOrderDetailBiz);
                    } else if(RefundTradeTypeEnum.REPAYMENT.getTradeType().equals(refundOrderDetailBiz.getTradeType())){
                        // 借款人还款并且是还垫付款，需要更新垫付的平台管理费|咨询服务费回款计划
                        updateProjectGuaranteeServicePlan(refundOrderDetailBiz);
                    }

                }

                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                    @Override
                    public void afterCommit() {
                        super.afterCommit();
                        // 发送还款订单明细处理完成消息
                        try {
                            refundDetailHandlerService.refundDetailHandlerMQ(refundOrder.getId());
                        } catch (Throwable e) {
                            logger.error("发送还款订单明细处理完成消息异常 projectId:{}, batchNo:{},", refundOrder.getProjectId(), refundOrder.getBatchNo(), e);
                        }

                        // 回款成功记账
                        Long investOrderId = null;
                        //还款或垫付使用投资人还款计划中投资订单ID
                        if (BusinessTypeEnum.REPAYMENT.name().equals(bizType) || BusinessTypeEnum.COMPENSATORY.name().equals(bizType)) {
                            investOrderId = projectInvestorPlan.getInvestOrderId();
                        } else if(BusinessTypeEnum.COMPENSATORY_REPAYMENT.name().equals(bizType)){
                            //还代偿款使用担保公司回款计划中投资订单ID
                            ProjectGuaranteePlan projectGuaranteePlan = projectGuaranteePlanMapperExt.selectByPrimaryKey(refundOrderDetailBiz.getBackPlanId());
                            investOrderId = projectGuaranteePlan.getOriginalOrderId();
                        }
                        receivedRepaymentSuccessAccounting(refundOrder, refundOrderDetail, refundOrderDetailBiz, project, investOrderId);
                    }
                });
            }

        } else {
            // 还款/垫付通知状态异常。
            logger.error("还款/垫付通知状态异常 projectId:{}, bankRefundNotifyDTO.getStatus():{}", refundOrderDetail.getProjectId(), bankRefundNotifyDTO.getStatus());
            throw new BizException("ACC10002", "还款/垫付通知状态异常");
        }
    }

    /**
     * 银行还款结果查询
     */
    public void bankRefundResultQueryJob() {
        List<RefundOrderDetail> refundOrderDetailList = refundOrderDetailMapperExt.queryRefundOrderDetailByBankRefundResultQueryJob();
        for (RefundOrderDetail refundOrderDetail : refundOrderDetailList) {
            try {
                BathTransactionNotifyDTO bathTransactionNotifyDTO = queryBankRefundResult(refundOrderDetail.getDetailRequestNo(), refundOrderDetail.getProjectId());
                ((RefundBankBackLocalService) AopContext.currentProxy()).bankRefundResultNotify(bathTransactionNotifyDTO);
            } catch (Exception e){
                logger.error("bankRefundResultQueryJob Exception detailRequestNo:{}", refundOrderDetail.getDetailRequestNo(), e);
            }
        }
    }

    /**
     * 查询银行还款结果
     *
     * @param detailRequestNo
     * @param projectId
     * @return
     */
    public BathTransactionNotifyDTO queryBankRefundResult(String detailRequestNo, Long projectId) {
        logger.info("调用银行查询标的还款结果 projectId:{}", projectId);
        CompletableFuture<QueryTransactionResponse> future = null;
        try {
            QueryTransactionRequest request = new QueryTransactionRequest();
            request.setRequestNo(detailRequestNo);
            request.setTransactionType(TradeQueryTypeEnum.TRANSACTION);
            future = lanMaoDirectService.queryTransaction(request);
        } catch (Throwable e) {
            logger.error("调用银行存管查询交易结果异常 projectId:{}", projectId, e);
            throw new BizException("ACC20009", e.getMessage());
        }

        try {
            QueryTransactionResponse response = future.get();
            logger.info("查询标的银行还款结果 projectId:{}; response:{}", projectId, JSON.toJSON(response));
            if (ResponseCodeEnum.SUCCESS.getValue().equals(response.getCode())) {
                List<QueryTransactionResponse.TransactionDetail> records = response.getRecords();
                if (records == null || records.isEmpty() || records.get(0) == null) {
                    throw new BizException("ACC20009", "调用银行存管查询交易结果失败:" + response.getErrorMessage());
                }
                BathTransactionNotifyDTO result = new BathTransactionNotifyDTO();
                result.setAsyncRequestNo(detailRequestNo);

                TransationStatusEnum status = TransationStatusEnum.enumOf(records.get(0).getStatus());
                switch (status) {
                    case SUCCESS:
                        result.setStatus(LendOrderStatusEnum.SUCCESS.name());
                        return result;
                    case FAIL:
                        logger.warn("查询标的银行还款结果 银行存管返回状态为处理失败  projectId:{}, records.get(0): {}", projectId, JSON.toJSON(records.get(0)));
                        result.setStatus(LendOrderStatusEnum.FAIL.name());
                        result.setErrorCode(records.get(0).getErrorCode());
                        result.setErrorMessage(records.get(0).getErrorMessage());
                        return result;
                    case ERROR:
                        logger.warn("查询标的银行还款结果 银行存管返回状态为处理异常  projectId:{}, records.get(0): {}", projectId, JSON.toJSON(records.get(0)));
                        result.setStatus(LendOrderStatusEnum.FAIL.name());
                        result.setErrorCode(records.get(0).getErrorCode());
                        result.setErrorMessage(records.get(0).getErrorMessage());
                        return result;
                    default:
                        logger.info("查询标的银行还款结果 银行存管返回状态为处理中  projectId:{}, records.get(0): {}", projectId, JSON.toJSON(records.get(0)));
                        throw new BizException("ACC20010", "调用银行存管查询交易结果,银行返回处理中:" + response.getErrorMessage());
                }
            } else {
                // 100007 查询对象不存在，需要重新发起还款
                if ("100007".equals(response.getErrorCode())) {
                    logger.error("查询标的银行还款结果不存在 projectId:{}; response:{}", projectId, JSON.toJSON(response));
                    ((RefundBankBackLocalService) AopContext.currentProxy()).orderNotFound(detailRequestNo, response.getErrorCode(), response.getErrorMessage());
                    throw new BizException("ACC20008", "查询标的银行还款结果不存在:" + response.getErrorMessage());
                } else {
                    // 调用查询失败
                    logger.error("查询标的银行还款结果失败 projectId:{}; response:{}", projectId, JSON.toJSON(response));
                    throw new BizException("ACC20009", "调用银行存管查询交易结果失败:" + response.getErrorMessage());
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            logger.error("调用银行存管查询交易结果异常 projectId:{}", projectId, e);
            throw new BizException("ACC20011", "调用银行存管查询交易结果异常");
        }
    }

    /**
     * 订单不存在修改订单明细状态
     *
     * @param detailRequestNo
     * @param errorCode
     * @param errorMessage
     */
    @Transactional
    public void orderNotFound(String detailRequestNo, String errorCode, String errorMessage) {
        RefundOrderDetail refundOrderDetail = refundOrderDetailMapperExt.queryRefundOrderDetailByDetailRequestNoForUpdate(detailRequestNo);
        if (refundOrderDetail.getCreateTime().before(new DateTime().minusMinutes(20).toDate())) {
            RefundOrderDetail updateRecord = new RefundOrderDetail();
            updateRecord.setId(refundOrderDetail.getId());
            updateRecord.setStatus(LendOrderStatusEnum.FAIL.getValue());
            updateRecord.setErrorCode(errorCode);
            updateRecord.setErrorMessage(errorMessage);
            refundOrderDetailMapperExt.updateByPrimaryKeySelective(updateRecord);
        }
    }

    /**
     * 回款成功记账任务
     */
    public void receivedRepaymentSuccessAccountingJob() {
        List<RefundOrderDetail> refundOrderDetailList = refundOrderDetailMapperExt.listForAccouting();
        for (RefundOrderDetail refundOrderDetail : refundOrderDetailList) {
            try {
                logger.info("回款成功记账任务 refundOrderDetail:{}", JSON.toJSON(refundOrderDetail));
                Project project = projectMapperExt.selectByPrimaryKey(refundOrderDetail.getProjectId());
                RefundOrder refundOrder = refundOrderMapperExt.selectByPrimaryKey(refundOrderDetail.getRefundOrderId());
                List<RefundOrderDetailBiz> refundOrderDetailBizList = refundOrderDetailBizMapperExt.queryRefundOrderDetailBizByDetailRequestNo(refundOrderDetail.getDetailRequestNo());
                RefundOrderDetailBiz refundOrderDetailBiz = null;
                for (RefundOrderDetailBiz temp : refundOrderDetailBizList) {
                    if (temp.getAmount().compareTo(BigDecimal.ZERO) > 0) {
                        refundOrderDetailBiz = temp;
                        break;
                    }
                }
                if (refundOrderDetailBiz != null) {
                    Long investOrderId = null;
                    //还款或垫付使用投资人还款计划中投资订单ID
                    if (BusinessTypeEnum.REPAYMENT.name().equals(refundOrderDetailBiz.getBizType()) || BusinessTypeEnum.COMPENSATORY.name().equals(refundOrderDetailBiz.getBizType())) {
                        ProjectInvestorPlan projectInvestorPlan = projectInvestorPlanMapperExt.selectByPrimaryKey(refundOrderDetailBiz.getBackPlanId());
                        investOrderId = projectInvestorPlan.getInvestOrderId();
                    } else if(BusinessTypeEnum.COMPENSATORY_REPAYMENT.name().equals(refundOrderDetailBiz.getBizType())){
                        //还代偿款使用担保公司回款计划中投资订单ID
                        ProjectGuaranteePlan projectGuaranteePlan = projectGuaranteePlanMapperExt.selectByPrimaryKey(refundOrderDetailBiz.getBackPlanId());
                        investOrderId = projectGuaranteePlan.getOriginalOrderId();
                    }
                    receivedRepaymentSuccessAccounting(refundOrder, refundOrderDetail, refundOrderDetailBiz, project, investOrderId);
                } else {
                    logger.info("数据Biz为空,detailtRequestNO={}", refundOrderDetail.getDetailRequestNo());
                }
            } catch (Throwable e) {
                logger.error("回款成功记账任务 lendOrder:{}", JSON.toJSON(refundOrderDetail), e);
            }
        }

    }

    /**
     * 回款成功记账
     *
     * @param refundOrder
     * @param refundOrderDetail
     * @param refundOrderDetailBiz
     * @param project
     * @param investOrderId
     */
    public void receivedRepaymentSuccessAccounting(RefundOrder refundOrder, RefundOrderDetail refundOrderDetail, RefundOrderDetailBiz refundOrderDetailBiz, Project project, Long investOrderId) {
        try {
            ReceivedRepaymentSuccessAccountingRequestDTO requestDTO = new ReceivedRepaymentSuccessAccountingRequestDTO();
            requestDTO.setOrderId(refundOrderDetail.getId());
            requestDTO.setDetailRequestNo(refundOrderDetail.getDetailRequestNo());
            requestDTO.setOrderType(OrderTypeEnum.RECEIVED_REPAYMENT);
            requestDTO.setOrderRefundTypeEnum(OrderRefundTypeEnum.enumOf(refundOrder.getRefundType()));
            requestDTO.setBusinessTypeEnum(BusinessTypeEnum.valueOf(refundOrderDetailBiz.getBizType()));
            requestDTO.setProjectId(refundOrderDetail.getProjectId());
            requestDTO.setBorrowProjectTypeEnum(BorrowProjectTypeEnum.enumOf(project.getType()));
            requestDTO.setProjectTitle(project.getTitle());
            requestDTO.setCurrentPeriod(refundOrderDetailBiz.getCurrentPeriod());
            requestDTO.setTotalPeriods(RepaymentTypeEnum.REPAY_PRINCIPAL_INTEREST_AT_TERM.getValue().equals(project.getRepaymentType()) ? 1 : project.getDeadline());
            requestDTO.setReceivedAccountId(refundOrderDetailBiz.getTargetAccountId());
            requestDTO.setRepaymentAccountId(refundOrderDetailBiz.getSourceAccountId());
            requestDTO.setInvestId(investOrderId);
            requestDTO.setAmount(refundOrderDetailBiz.getAmount());
            requestDTO.setExpectInterest(refundOrderDetailBiz.getExpectInterest());
            List<BaseFeeDetailDTO> feeDetailDTOList = new ArrayList<>();
            addAccountingFeeDetail(refundOrderDetailBiz.getCapital(), FeeTypeEnum.CAPITAL, feeDetailDTOList);
            addAccountingFeeDetail(refundOrderDetailBiz.getInterest(), FeeTypeEnum.INTEREST, feeDetailDTOList);
            addAccountingFeeDetail(refundOrderDetailBiz.getPenaltyFee(), FeeTypeEnum.PENALTY, feeDetailDTOList);
            addAccountingFeeDetail(refundOrderDetailBiz.getManageFee(), FeeTypeEnum.MANAGEMENT_FEE, feeDetailDTOList);
            addAccountingFeeDetail(refundOrderDetailBiz.getGuaranteeFee(), FeeTypeEnum.GUARANTEE_FEE, feeDetailDTOList);
            addAccountingFeeDetail(refundOrderDetailBiz.getConsultingFee(), FeeTypeEnum.CONSULTING_FEE, feeDetailDTOList);
            requestDTO.setFeeDetailDTOList(feeDetailDTOList);

            transOrderDubboService.receivedRepaymentSuccessAccounting(requestDTO);
            ((RefundBankBackLocalService) AopContext.currentProxy()).updateReceivedRepaymentAccountingSuccessful(refundOrderDetail.getId());
        } catch (Throwable e) {
            logger.error("回款成功记账异常 projectId:{}", refundOrderDetail.getProjectId(), e);
        }
    }

    /**
     * 添加记账费用明细
     *
     * @param feeItme
     * @param feeTypeEnum
     * @param feeDetailDTOList
     */
    private void addAccountingFeeDetail(BigDecimal feeItme, FeeTypeEnum feeTypeEnum, List<BaseFeeDetailDTO> feeDetailDTOList) {
        if (feeItme.compareTo(BigDecimal.ZERO) > 0) {
            BaseFeeDetailDTO baseFeeDetailDTO = new BaseFeeDetailDTO();
            baseFeeDetailDTO.setAmount(feeItme);
            baseFeeDetailDTO.setFeeTypeEnum(feeTypeEnum);
            feeDetailDTOList.add(baseFeeDetailDTO);
        }
    }

    /**
     * 更新收款人回款记账成功
     *
     * @param refundOrderDetailId
     */
    @Transactional
    public void updateReceivedRepaymentAccountingSuccessful(Long refundOrderDetailId) {
        RefundOrderDetail refundOrderDetail = new RefundOrderDetail();
        refundOrderDetail.setId(refundOrderDetailId);
        refundOrderDetail.setAccountingStatus(RefundOrderAccountingStatusEnum.SUCCESS.getValue());
        refundOrderDetailMapperExt.updateByPrimaryKeySelective(refundOrderDetail);
    }

    /**
     * 更新投资人回款计划
     *
     * @param projectInvestorPlan
     * @param refundOrderDetailBiz
     * @param currentDate
     */
    @Transactional(propagation = Propagation.MANDATORY, isolation = READ_COMMITTED)
    public void updateInvestorPlanAndInvestOrderEvolutionStatus(ProjectInvestorPlan projectInvestorPlan, RefundOrderDetailBiz refundOrderDetailBiz, Date currentDate, Byte refundType) {
        logger.info("updateInvestorPlan projectId:{}", refundOrderDetailBiz.getProjectId());
        // 投资订单维度做同步处理
        ProjectInvestOrder projectInvestOrder = projectInvestOrderMapperExt.selectByPrimaryKeyForUpdate(projectInvestorPlan.getInvestOrderId());

        //债转数据更新
        InvestorCreditor investorCreditorOriginal = null;
        if (BusinessTypeEnum.REPAYMENT.name().equals(refundOrderDetailBiz.getBizType())
                || BusinessTypeEnum.PROXY_REPAYMENT.name().equals(refundOrderDetailBiz.getBizType()) //目前没有PROXY_REPAYMENT这种类型，加上不影响
                || BusinessTypeEnum.COMPENSATORY.name().equals(refundOrderDetailBiz.getBizType())
                || RefundTypeEnum.OVERDEPT.getRefundType().equals(refundType)){//提前结清
            investorCreditorOriginal = investorCreditorMapperExt.queryInvestorCreditorByInvestOrderId(projectInvestorPlan.getInvestOrderId());
        }
        Project project = projectMapperExt.selectByPrimaryKey(refundOrderDetailBiz.getProjectId());
        PeriodUnitEnum periodUnit = PeriodUnitEnum.enumOf(project.getDeadlineUnit());

        ProjectInvestOrder updateProjectInvestOrder = new ProjectInvestOrder();
        updateProjectInvestOrder.setId(projectInvestorPlan.getInvestOrderId());

        Date receivedDate = projectInvestorPlan.getReceivedDate();//当期回款日

        ProjectInvestorPlan updateRecord = new ProjectInvestorPlan();
        updateRecord.setId(projectInvestorPlan.getId());
        updateRecord.setReceivedCapital(projectInvestorPlan.getReceivedCapital().add(refundOrderDetailBiz.getCapital()));
        updateRecord.setReceivedInterest(projectInvestorPlan.getReceivedInterest().add(refundOrderDetailBiz.getInterest()));

        if (RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderDetailBiz.getTradeType())) {// 垫付时不对罚息罚息进行垫付,垫付不提前结清
            updateRecord.setIsGuarantee((byte) 1);
            updateRecord.setTotalPenaltyFee(projectInvestorPlan.getReceivedPenaltyFee());
            if (updateRecord.getReceivedCapital().add(updateRecord.getReceivedInterest()).
                    compareTo(projectInvestorPlan.getCapital().add(projectInvestorPlan.getInterest())) >= 0) {
                if (currentDate.before(receivedDate) || DateUtils.isSameDay(currentDate, receivedDate)) {
                    //回款日是在当期回款日之前或当期回款日当天为正常收款
                    updateRecord.setReceivedStatus(Byte.valueOf("2"));
                } else {
                    //回款日是在当期回款日之后为逾期收款
                    updateRecord.setReceivedStatus(Byte.valueOf("3"));
                }
                updateRecord.setOverTime(new Date());
                updateRecord.setOverCurrentDate(currentDate);
            }
        } else {
            updateRecord.setReceivedPenaltyFee(projectInvestorPlan.getReceivedPenaltyFee().add(refundOrderDetailBiz.getPenaltyFee()));
            if(BigDecimal.ZERO.compareTo(refundOrderDetailBiz.getCapital()) != 0){
                updateRecord.setPenaltyBeginDate(currentDate);
                updateRecord.setOrgPenaltyFee(projectInvestorPlan.getTotalPenaltyFee());
            }
            updateRecord.setIsGuarantee((byte) 0);
            if ((updateRecord.getReceivedCapital().add(updateRecord.getReceivedInterest()).add(updateRecord.getReceivedPenaltyFee()).add(refundOrderDetailBiz.getExpectInterest())).
                    compareTo(projectInvestorPlan.getCapital().add(projectInvestorPlan.getInterest()).add(projectInvestorPlan.getTotalPenaltyFee())) >= 0) {

                if (PeriodUnitEnum.MONTH.equals(periodUnit)
                        && projectInvestorPlan.getCurrentPeriod() != 1
                        && (receivedDate.after(new DateTime(currentDate).plusMonths(1).minusDays(1).toDate()))) {
                    //月标非第一期回款回款日是在上一期回款日之前或上一期回款日当天为提前回款
                    updateRecord.setReceivedStatus(Byte.valueOf("4"));
                } else if (currentDate.before(receivedDate) || DateUtils.isSameDay(currentDate, receivedDate)) {
                    //回款日是在当期回款日之前或当期回款日当天为正常收款
                    updateRecord.setReceivedStatus(Byte.valueOf("2"));
                } else {
                    //回款日是在当期回款日之后为逾期收款
                    updateRecord.setReceivedStatus(Byte.valueOf("3"));
                }
                updateRecord.setOverTime(new Date());
                updateRecord.setOverCurrentDate(currentDate);
            }
            //先息后本且为提前结清时
            if (RefundTypeEnum.OVERDEPT.getRefundType().equals(refundType) && RepaymentTypeEnum.MONTHLY_INTEREST_PAYMENT_DUE.getValue().equals(project.getRepaymentType())){
                //提前结清时，把订单对应的回款计划状态(无回款金额)一次更新完
                logger.info("提前结清存在中间期数无利息数据,refundOrderDetailBiz={}", JSON.toJSONString(refundOrderDetailBiz));
                List<ProjectInvestorPlan>  projectInvestorPlanList = projectInvestorPlanMapperExt.listProjectInvestorPlanNotReceived(projectInvestorPlan.getInvestOrderId(),projectInvestorPlan.getProjectId());
                for (ProjectInvestorPlan investorPlan : projectInvestorPlanList) {
                    //非指定还款计划之外的数据更新 (只更处理未收款状态的)
                    if (!investorPlan.getId().equals(projectInvestorPlan.getId()) && InvestorPlanReceivedStatusEnum.NON_RECEIVE.getCode().equals(investorPlan.getReceivedStatus())){
                        ProjectInvestorPlan updater = new ProjectInvestorPlan();
                        updater.setId(investorPlan.getId());
                        updater.setReceivedStatus(Byte.valueOf("4"));//提前回款
                        updater.setOverTime(new Date());
                        updater.setOverCurrentDate(currentDate);
                        projectInvestorPlanMapperExt.updateByPrimaryKeySelective(updater);
                    }
                }
            }
        }
        projectInvestorPlanMapperExt.updateByPrimaryKeySelective(updateRecord);

        //维护债权订单状态和投资订单状态的一致性
        if(!ProjectInvestOrderEvolutionStatusEnum.ALL_BACKED.getValue().equals(projectInvestOrder.getEvolutionStatus())) {
            List<ProjectInvestorPlan> projectInvestorPlanList = projectInvestorPlanMapperExt.listProjectInvestorPlanNotReceived(projectInvestorPlan.getInvestOrderId(), projectInvestorPlan.getProjectId());
            if (projectInvestorPlanList.isEmpty()) {
                updateProjectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.ALL_BACKED.getValue());
                projectInvestOrderMapperExt.updateByPrimaryKeySelective(updateProjectInvestOrder);

                //回款中--》结清 新增
                if (investorCreditorOriginal!=null){
                    InvestorCreditor investorCreditorUpdater = new InvestorCreditor();
                    investorCreditorUpdater.setId(investorCreditorOriginal.getId());
                    investorCreditorUpdater.setStatus(InvestorCreditorStatusEnum.FINISH.getStatus());
                    investorCreditorUpdater.setOverTime(new Date());
                    investorCreditorMapperExt.updateByPrimaryKeySelective(investorCreditorUpdater);
                }
                //结清时
                this.handForPackageInvestOrderStatus(updateProjectInvestOrder.getId(),PackageInvestOrderEvolutionStatusEnum.ALL_BACKED);

            } else {
                boolean backOverdueFlag = false;//是否需要维护订单逾期-->正常
                //repaymentFlag 非垫付类型且 当前时间>=回款时间 是否可以从逾期 --》正常
                if (!RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderDetailBiz.getTradeType())
                        && !currentDate.after(projectInvestorPlanList.get(0).getReceivedDate())){
                    backOverdueFlag = true;
                }else if (RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderDetailBiz.getTradeType())
                        && currentDate.before(projectInvestorPlanList.get(0).getReceivedDate())){
                    //guaranteeFlag 垫付类型  逾期--》正常
                    backOverdueFlag = true;
                }
                if (backOverdueFlag) {
                    updateProjectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.BACKING.getValue());
                    projectInvestOrderMapperExt.updateByPrimaryKeySelective(updateProjectInvestOrder);
                    //逾期回款中--》正常回款中  新增
                    if (investorCreditorOriginal!=null && InvestorCreditorStatusEnum.RECEIVE_OVERDUE.getStatus().equals(investorCreditorOriginal.getStatus())){
                        InvestorCreditor investorCreditorUpdater = new InvestorCreditor();
                        investorCreditorUpdater.setId(investorCreditorOriginal.getId());
                        investorCreditorUpdater.setStatus(InvestorCreditorStatusEnum.NORMAL_RECEIVING.getStatus());
                        investorCreditorMapperExt.updateByPrimaryKeySelective(investorCreditorUpdater);
                    }
                    // 逾期--》正常
                    this.handForPackageInvestOrderStatus(updateProjectInvestOrder.getId(),PackageInvestOrderEvolutionStatusEnum.BACKING);
                }
            }
        }

        //如果investorCreditorOriginal非空，则需要维护债权转让列表
        if (investorCreditorOriginal != null){
            updateInvestCreditor(investorCreditorOriginal,refundOrderDetailBiz,projectInvestorPlan, refundType);
        }

        // 回款本金、利息、罚息不为0 添加回款人通知数据
        if(refundOrderDetailBiz.getCapital().add(refundOrderDetailBiz.getInterest()).add(refundOrderDetailBiz.getPenaltyFee()).compareTo(BigDecimal.ZERO) != 0){
            addInvestorReceiveRecord(refundOrderDetailBiz);
        }

        // 如果是按日计息还款，更新投资人债权状态、投资订单衍生状态、回款计划状态
        if(refundCommonService.isCalculateInterestByDay(project.getOrgId(), project.getDeadlineUnit(), projectInvestorPlan.getReceivedDate(), currentDate)){
            if (investorCreditorOriginal!=null){
                InvestorCreditor updaterInvestorCreditor = new InvestorCreditor();
                updaterInvestorCreditor.setId(investorCreditorOriginal.getId());
                updaterInvestorCreditor.setStatus(InvestorCreditorStatusEnum.FINISH.getStatus());
                updaterInvestorCreditor.setOverTime(new Date());
                investorCreditorMapperExt.updateByPrimaryKeySelective(updaterInvestorCreditor);
            }

            updateProjectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.ALL_BACKED.getValue());
            projectInvestOrderMapperExt.updateByPrimaryKeySelective(updateProjectInvestOrder);
            //处理投资订单包衍生状态    还款中--》结清
            this.handForPackageInvestOrderStatus(updateProjectInvestOrder.getId(),PackageInvestOrderEvolutionStatusEnum.ALL_BACKED);

            ProjectInvestorPlan updaterProjectInvestorPlan = new ProjectInvestorPlan();
            updaterProjectInvestorPlan.setId(projectInvestorPlan.getId());
            updaterProjectInvestorPlan.setReceivedStatus(Byte.valueOf("4"));
            updaterProjectInvestorPlan.setOverTime(new Date());
            updaterProjectInvestorPlan.setOverCurrentDate(currentDate);
            projectInvestorPlanMapperExt.updateByPrimaryKeySelective(updaterProjectInvestorPlan);
        }

    }


    /**
     * 处理投资包 衍生状态
     *
     * @param investOrderId
     */
    private void handForPackageInvestOrderStatus(Long investOrderId,PackageInvestOrderEvolutionStatusEnum targetStatus){
        ProjectPackageInvestOrderDetail detail = projectPackageInvestOrderDetailMapperExt.selectByInvestOrderId(investOrderId);
        if (detail != null){
            List<ProjectPackageInvestOrderDetail> details = projectPackageInvestOrderDetailMapperExt.selectByPackageInvestOrderId(detail.getPackageInvestOrderId());
            logger.info("查询ProjectPackageInvestOrderDetail.list={}",JSON.toJSONString(details));
            Set<Long> investOrderIdSet = new HashSet<>();
            for (ProjectPackageInvestOrderDetail orderDetail : details) {
                investOrderIdSet.add(orderDetail.getInvestOrderId());
            }

            int overdueOrderNums = 0;// 逾期中数量
            int repayingOrderNums = 0;//还款中数量（含逾期还款中）
            List<ProjectInvestOrder> projectInvestOrderList = projectInvestOrderMapperExt.selectByInvestOrderIds(investOrderIdSet);
            logger.info("包订单列表,packageInvestOrderId={},projectInvestOrderList.size={}",detail.getPackageInvestOrderId(),projectInvestOrderList.size());
            if (CollectionUtils.isEmpty(projectInvestOrderList)){
                return;
            }
            for (ProjectInvestOrder order : projectInvestOrderList) {
                if (targetStatus == PackageInvestOrderEvolutionStatusEnum.BACKING
                        && order.getEvolutionStatus().equals(ProjectInvestOrderEvolutionStatusEnum.BACKING_OVERDUE.getValue())){
                    overdueOrderNums++;
                }
                logger.info("targetStatus == PackageInvestOrderEvolutionStatusEnum.ALL_BACKED:getPackageInvestOrderId={},{}",detail.getPackageInvestOrderId(),targetStatus == PackageInvestOrderEvolutionStatusEnum.ALL_BACKED);
                if (targetStatus == PackageInvestOrderEvolutionStatusEnum.ALL_BACKED
                        && (order.getEvolutionStatus().equals(ProjectInvestOrderEvolutionStatusEnum.BACKING_OVERDUE.getValue())
                             || order.getEvolutionStatus().equals(ProjectInvestOrderEvolutionStatusEnum.BACKING.getValue()))){
                    logger.info("包投资订单存在还款中,{}",detail.getPackageInvestOrderId());
                    repayingOrderNums++;
                }
            }
            //投资包下中逾期投资订单为0时,更新投资包状态 逾期 --》正常
            if (targetStatus == PackageInvestOrderEvolutionStatusEnum.BACKING
                    && overdueOrderNums == 0) {
                logger.info("更新投资包数据 ProjectPackageInvestOrderDetail.id={}", detail.getId());
                ProjectPackageInvestOrder projectPackageInvestOrder = projectPackageInvestOrderMapperExt.selectByPrimaryKey(detail.getPackageInvestOrderId());
                if (projectPackageInvestOrder.getEvolutionStatus().equals(PackageInvestOrderEvolutionStatusEnum.BACKING_OVERDUE.getValue())) {
                    logger.info("更新投资包数据 projectPackageInvestOrder状态为正常还款中,id={}", projectPackageInvestOrder.getId());
                    ProjectPackageInvestOrder projectPackageInvestOrderUpdater = new ProjectPackageInvestOrder();
                    projectPackageInvestOrderUpdater.setId(projectPackageInvestOrder.getId());
                    projectPackageInvestOrderUpdater.setEvolutionStatus(PackageInvestOrderEvolutionStatusEnum.BACKING.getValue());
                    projectPackageInvestOrderMapperExt.updateByPrimaryKeySelective(projectPackageInvestOrderUpdater);
                }
            }
            //投资包下中逾期投资订单无还款中时,更新投资包状态 还款中--》结清
            if (targetStatus == PackageInvestOrderEvolutionStatusEnum.ALL_BACKED
                    && repayingOrderNums == 0){
                logger.info("更新投资包数据 ProjectPackageInvestOrderDetail.packageInvestOrderId={}", detail.getPackageInvestOrderId());
                ProjectPackageInvestOrder projectPackageInvestOrder = projectPackageInvestOrderMapperExt.selectByPrimaryKey(detail.getPackageInvestOrderId());
                if (projectPackageInvestOrder.getEvolutionStatus().equals(PackageInvestOrderEvolutionStatusEnum.BACKING_OVERDUE.getValue())
                        || projectPackageInvestOrder.getEvolutionStatus().equals(PackageInvestOrderEvolutionStatusEnum.BACKING.getValue())) {
                    logger.info("更新投资包数据 projectPackageInvestOrder状态为结清,id={}", projectPackageInvestOrder.getId());
                    ProjectPackageInvestOrder projectPackageInvestOrderUpdater = new ProjectPackageInvestOrder();
                    projectPackageInvestOrderUpdater.setId(projectPackageInvestOrder.getId());
                    projectPackageInvestOrderUpdater.setEvolutionStatus(PackageInvestOrderEvolutionStatusEnum.ALL_BACKED.getValue());
                    projectPackageInvestOrderMapperExt.updateByPrimaryKeySelective(projectPackageInvestOrderUpdater);
                }
            }
        }
    }

    /**
     * 投资人收款-维护可转让债权列表数据
     * @param investorCreditorOriginal
     * @param refundOrderDetailBiz
     * @param projectInvestorPlan
     */
    private void updateInvestCreditor(InvestorCreditor investorCreditorOriginal ,RefundOrderDetailBiz refundOrderDetailBiz ,ProjectInvestorPlan projectInvestorPlan,Byte refundType){
        logger.info("investorCreditorOriginal={},projectId={},projectInvestorPlan={},detailRequestNo={}",JSON.toJSONString(investorCreditorOriginal),projectInvestorPlan.getProjectId(),JSON.toJSON(projectInvestorPlan),refundOrderDetailBiz.getDetailRequestNo());
        InvestorCreditor investorCreditorUpdater = new InvestorCreditor();
        investorCreditorUpdater.setId(investorCreditorOriginal.getId());
        investorCreditorUpdater.setReceivedCapital(investorCreditorOriginal.getReceivedCapital().add(refundOrderDetailBiz.getCapital()));
        investorCreditorUpdater.setReceivedInterest(investorCreditorOriginal.getReceivedInterest().add(refundOrderDetailBiz.getInterest()));
        investorCreditorUpdater.setReceivedPenaltyFee(investorCreditorOriginal.getReceivedPenaltyFee().add(refundOrderDetailBiz.getPenaltyFee()));


        //最后一期状态维护
        if (projectInvestorPlan.getCurrentPeriod().equals(projectInvestorPlan.getTotalPeriod())){
            if(refundOrderDetailBiz.getCapital().compareTo(projectInvestorPlan.getCapital().subtract(projectInvestorPlan.getReceivedCapital())) == 0
                    && refundOrderDetailBiz.getInterest().compareTo(projectInvestorPlan.getInterest().subtract(projectInvestorPlan.getReceivedInterest())) == 0){
                if (RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderDetailBiz.getTradeType())) {
                    investorCreditorUpdater.setStatus(InvestorCreditorStatusEnum.FINISH.getStatus());//已结清
                    investorCreditorUpdater.setOverTime(new Date());
                    this.setInvestDatePubForOnePeriodOver(investorCreditorUpdater,investorCreditorOriginal,projectInvestorPlan);
                }else if ((RefundTradeTypeEnum.REPAYMENT.getTradeType().equals(refundOrderDetailBiz.getTradeType()) || RefundTradeTypeEnum.REPLACE.getTradeType().equals(refundOrderDetailBiz.getTradeType()))
                        && refundOrderDetailBiz.getPenaltyFee().compareTo(projectInvestorPlan.getTotalPenaltyFee().subtract(projectInvestorPlan.getReceivedPenaltyFee())) == 0){
                    investorCreditorUpdater.setStatus(InvestorCreditorStatusEnum.FINISH.getStatus());//已结清
                    investorCreditorUpdater.setOverTime(new Date());
                    this.setInvestDatePubForOnePeriodOver(investorCreditorUpdater,investorCreditorOriginal,projectInvestorPlan);
                }else {
                    logger.info("最后一期罚息未还完，投资订单状态不变更,project={},investOrderId={}",investorCreditorOriginal.getProjectId(),investorCreditorOriginal.getInvestOrderId());
                }
            }
        }else {
            //非最后一期维护(本利罚都收完才下移一期)
            if(refundOrderDetailBiz.getCapital().compareTo(projectInvestorPlan.getCapital().subtract(projectInvestorPlan.getReceivedCapital())) == 0
                    && refundOrderDetailBiz.getInterest().compareTo(projectInvestorPlan.getInterest().subtract(projectInvestorPlan.getReceivedInterest())) == 0){
                if (RefundTypeEnum.OVERDEPT.getRefundType().equals(refundType)){
                    logger.info("提前结清还款，investOrderId={},detailRequestNo={}",investorCreditorOriginal.getInvestOrderId(),refundOrderDetailBiz.getDetailRequestNo());
                    this.setInvestDatePubForOnePeriodOver(investorCreditorUpdater,investorCreditorOriginal,projectInvestorPlan);
                }else {
                    //最小期数（未收款）
                    Integer minPeriodNotReceived = projectInvestorPlanMapperExt.getMinPeriodNotReceived(investorCreditorOriginal.getInvestOrderId());
                    if (minPeriodNotReceived == null) {//逾期多期还款时，可能进入此逻辑
                        this.setInvestDatePubForOnePeriodOver(investorCreditorUpdater,investorCreditorOriginal,projectInvestorPlan);
                        logger.warn("查询最小期为空，投资订单号={},detailRequestNo={}", investorCreditorOriginal.getInvestOrderId(), refundOrderDetailBiz.getDetailRequestNo());
                    }else {
                        ProjectInvestorPlan nextPeriodPlan = projectInvestorPlanMapperExt.getCurrentPeriodInvestorPlan(investorCreditorOriginal.getInvestOrderId(), investorCreditorOriginal.getInvestUserId(), minPeriodNotReceived);
                        logger.info("债权转让列表维护，minPeriodNotReceived={},investorOrderId={}", minPeriodNotReceived, investorCreditorOriginal.getInvestOrderId());
                        //1、垫付时  2、还款或代还款且罚息已还
                        if (RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderDetailBiz.getTradeType())
                                || ((RefundTradeTypeEnum.REPAYMENT.getTradeType().equals(refundOrderDetailBiz.getTradeType()) || RefundTradeTypeEnum.REPLACE.getTradeType().equals(refundOrderDetailBiz.getTradeType()))
                                        && refundOrderDetailBiz.getPenaltyFee().compareTo(projectInvestorPlan.getTotalPenaltyFee().subtract(projectInvestorPlan.getReceivedPenaltyFee())) == 0)) {
                            this.setInvestDatePubForOnePeriodOver(investorCreditorUpdater, investorCreditorOriginal, projectInvestorPlan);
                            investorCreditorUpdater.setCurrentPeriod(nextPeriodPlan.getCurrentPeriod());//当前期下移一期
                            investorCreditorUpdater.setReceivedDate(nextPeriodPlan.getReceivedDate());
                            investorCreditorUpdater.setCurrentInterest(nextPeriodPlan.getInterest().subtract(nextPeriodPlan.getReceivedInterest()));
                        } else {
                            logger.info("本期罚息未还完，债权当前期数不下移,project={},investOrderId={}", investorCreditorOriginal.getProjectId(), investorCreditorOriginal.getInvestOrderId());
                        }
                    }
                }
            }
        }
        investorCreditorMapperExt.updateByPrimaryKeySelective(investorCreditorUpdater);
    }

    private void setInvestDatePubForOnePeriodOver(InvestorCreditor investorCreditorUpdater,InvestorCreditor investorCreditorOriginal,ProjectInvestorPlan projectInvestorPlan ){
        investorCreditorUpdater.setReceivedPeriod(investorCreditorOriginal.getReceivedPeriod()+1);//已收期数+1
        Date lastPeriodReceivedDateOri = investorCreditorOriginal.getLastPeriodReceivedDate();
        if (lastPeriodReceivedDateOri == null || lastPeriodReceivedDateOri.before(projectInvestorPlan.getReceivedDate())) {
            investorCreditorUpdater.setLastPeriodReceivedDate(projectInvestorPlan.getReceivedDate());
        }
    }

    @Transactional(propagation = Propagation.MANDATORY, isolation = READ_COMMITTED)
    public void addInvestorReceiveRecord(RefundOrderDetailBiz refundOrderDetailBiz){
        InvestorReceiveRecord investorReceiveRecord = investorReceiveRecordMapperExt.selectByUniqueIndexForUpdate(refundOrderDetailBiz.getTargetUserId(), refundOrderDetailBiz.getProjectId(), new Date());
        if(investorReceiveRecord == null){
            InvestorReceiveRecord insertRecord = new InvestorReceiveRecord();
            insertRecord.setId(snowflakeIdWorker.nextId());
            insertRecord.setInvestorUserId(refundOrderDetailBiz.getTargetUserId());
            insertRecord.setProjectId(refundOrderDetailBiz.getProjectId());
            insertRecord.setReceivedCapital(refundOrderDetailBiz.getCapital());
            insertRecord.setReceivedInterest(refundOrderDetailBiz.getInterest());
            insertRecord.setReceivedPenaltyFee(refundOrderDetailBiz.getPenaltyFee());
            insertRecord.setReceivedDate(new Date());
            insertRecord.setCreateTime(new Date());

            investorReceiveRecordMapperExt.insertSelective(insertRecord);
        } else {
            InvestorReceiveRecord updateRecord = new InvestorReceiveRecord();
            updateRecord.setId(investorReceiveRecord.getId());
            updateRecord.setReceivedCapital(investorReceiveRecord.getReceivedCapital().add(refundOrderDetailBiz.getCapital()));
            updateRecord.setReceivedInterest(investorReceiveRecord.getReceivedInterest().add(refundOrderDetailBiz.getInterest()));
            updateRecord.setReceivedPenaltyFee(investorReceiveRecord.getReceivedPenaltyFee().add(refundOrderDetailBiz.getPenaltyFee()));

            investorReceiveRecordMapperExt.updateByPrimaryKeySelective(updateRecord);
        }
    }

    /**
     * 更新担保公司回款计划
     *
     * @param refundOrderDetailBiz
     */
    @Transactional(propagation = Propagation.MANDATORY)
    public void updateGuaranteePlan(RefundOrderDetailBiz refundOrderDetailBiz, Date currentDate) {
        logger.info("updateGuaranteePlan projectId:{}", refundOrderDetailBiz.getProjectId());
        ProjectGuaranteePlan projectGuaranteePlan = projectGuaranteePlanMapperExt.selectByPrimaryKey(refundOrderDetailBiz.getBackPlanId());

        ProjectGuaranteePlan updateRecord = new ProjectGuaranteePlan();
        updateRecord.setId(projectGuaranteePlan.getId());
        updateRecord.setReceivedCapital(projectGuaranteePlan.getReceivedCapital().add(refundOrderDetailBiz.getCapital()));
        updateRecord.setReceivedInterest(projectGuaranteePlan.getReceivedInterest().add(refundOrderDetailBiz.getInterest()));
        updateRecord.setReceivedPenaltyFee(projectGuaranteePlan.getReceivedPenaltyFee().add(refundOrderDetailBiz.getPenaltyFee()));

        if(BigDecimal.ZERO.compareTo(refundOrderDetailBiz.getCapital()) != 0){
            updateRecord.setPenaltyBeginDate(currentDate);
            updateRecord.setOrigianlPenalty(projectGuaranteePlan.getTotalPenaltyFee());
        }

        if (updateRecord.getReceivedCapital().add(updateRecord.getReceivedInterest())
                .add(updateRecord.getReceivedPenaltyFee()).
                        compareTo(projectGuaranteePlan.getCapital().add(projectGuaranteePlan.getInterest())
                                .add(projectGuaranteePlan.getTotalPenaltyFee())) >= 0) {
            updateRecord.setReceivedStatus((byte) 2);
            updateRecord.setOverTime(new Date());
            updateRecord.setOverCurrentDate(currentDate);
        }

        projectGuaranteePlanMapperExt.updateByPrimaryKeySelective(updateRecord);
    }

    /**
     * 创建担保公司回款计划
     *
     * @param refundOrderDetailBiz
     * @param projectInvestorPlan
     * @return
     */
    @Transactional(propagation = Propagation.MANDATORY)
    public void buildProjectGuaranteePlan(RefundOrderDetailBiz refundOrderDetailBiz, ProjectInvestorPlan projectInvestorPlan) {
        logger.info("buildProjectGuaranteePlan projectId:{}", refundOrderDetailBiz.getProjectId());
        ProjectGuaranteePlan projectGuaranteePlan = new ProjectGuaranteePlan();
        projectGuaranteePlan.setId(snowflakeIdWorker.nextId());
        projectGuaranteePlan.setOriginalOrderId(projectInvestorPlan.getInvestOrderId());
        projectGuaranteePlan.setAdvanceOrderId(refundOrderDetailBiz.getRefundOrderId());
        projectGuaranteePlan.setProjectId(refundOrderDetailBiz.getProjectId());
        projectGuaranteePlan.setUserId(refundOrderDetailBiz.getSourceUserId());
        projectGuaranteePlan.setPlatformUserNo(refundOrderDetailBiz.getSourcePlatformUserNo());
        projectGuaranteePlan.setAccountId(refundOrderDetailBiz.getSourceAccountId());
        projectGuaranteePlan.setPeriod(refundOrderDetailBiz.getCurrentPeriod());
        projectGuaranteePlan.setReceiveDate(projectInvestorPlan.getReceivedDate());
        projectGuaranteePlan.setTotalPeriod(projectInvestorPlan.getTotalPeriod());
        projectGuaranteePlan.setPenaltyBeginDate(projectInvestorPlan.getPenaltyBeginDate());
        projectGuaranteePlan.setOrigianlPenalty(projectInvestorPlan.getOrgPenaltyFee().subtract(projectInvestorPlan.getReceivedPenaltyFee()));
        projectGuaranteePlan.setReceivedInterest(BigDecimal.ZERO);
        projectGuaranteePlan.setReceivedCapital(BigDecimal.ZERO);
        projectGuaranteePlan.setReceivedPenaltyFee(BigDecimal.ZERO);
        projectGuaranteePlan.setTotalPenaltyFee(projectInvestorPlan.getTotalPenaltyFee().subtract(projectInvestorPlan.getReceivedPenaltyFee()));
        projectGuaranteePlan.setCapital(refundOrderDetailBiz.getCapital());
        projectGuaranteePlan.setInterest(refundOrderDetailBiz.getInterest());
        if(RefundTradeTypeEnum.ADVANCE.getTradeType().equals(refundOrderDetailBiz.getTradeType())){
            projectGuaranteePlan.setReceivedStatus((byte) 1);
        } else{
            projectGuaranteePlan.setReceivedStatus((byte) 2);
        }
        projectGuaranteePlan.setInvestTime(projectInvestorPlan.getInvestTime());
        projectGuaranteePlan.setCreateTime(new Date());
        projectGuaranteePlan.setUpdateTime(new Date());

        projectGuaranteePlanMapperExt.insertSelective(projectGuaranteePlan);
    }

    /**
     * 创建担保公司代偿服务费回款计划表
     *
     * @param refundOrderDetailBiz
     * @return
     */
    @Transactional(propagation = Propagation.MANDATORY)
    public void buildProjectGuaranteeServicePlan(RefundOrderDetailBiz refundOrderDetailBiz) {
        ProjectGuaranteeServicePlan record = new ProjectGuaranteeServicePlan();
        record.setId(snowflakeIdWorker.nextId());
        record.setProjectId(refundOrderDetailBiz.getProjectId());
        record.setBorrowUserId(refundOrderDetailBiz.getBorrowUserId());
        record.setCurrentPeriod(refundOrderDetailBiz.getCurrentPeriod());
        record.setRefundOrderId(refundOrderDetailBiz.getRefundOrderId());
        record.setDetailRequestNo(refundOrderDetailBiz.getDetailRequestNo());
        record.setBizType(refundOrderDetailBiz.getBizType());
        if(refundOrderDetailBiz.getManageFee().compareTo(BigDecimal.ZERO)>0){
            record.setFeeType(FeeTypeEnum.MANAGEMENT_FEE.getValue());
        }else if(refundOrderDetailBiz.getGuaranteeFee().compareTo(BigDecimal.ZERO)>0){
            record.setFeeType(FeeTypeEnum.GUARANTEE_FEE.getValue());
        }else if(refundOrderDetailBiz.getConsultingFee().compareTo(BigDecimal.ZERO)>0){
            record.setFeeType(FeeTypeEnum.CONSULTING_FEE.getValue());
        }else{
            throw new BizException("费用科目类型异常");
        }
        record.setSourceUserId(refundOrderDetailBiz.getSourceUserId());
        record.setSourceAccountId(refundOrderDetailBiz.getSourceAccountId());
        record.setSourcePlatformUserNo(refundOrderDetailBiz.getSourcePlatformUserNo());
        record.setAmount(refundOrderDetailBiz.getAmount());
        if(RefundTradeTypeEnum.REPLACE.getTradeType().equals(refundOrderDetailBiz.getTradeType())){
            record.setReceiveStatus(Byte.valueOf("1"));
        }
        record.setCreateTime(new Date());
        record.setUpdateTime(new Date());
        projectGuaranteeServicePlanMapper.insertSelective(record);
    }

    /**
     * 修改担保公司代偿服务费回款计划表
     *
     * @param refundOrderDetailBiz
     * @return
     */
    @Transactional(propagation = Propagation.MANDATORY)
    public void updateProjectGuaranteeServicePlan(RefundOrderDetailBiz refundOrderDetailBiz) {
        FeeTypeEnum feeTypeEnum;
        if(refundOrderDetailBiz.getManageFee().compareTo(BigDecimal.ZERO)>0){
            feeTypeEnum = FeeTypeEnum.MANAGEMENT_FEE;
        }else if(refundOrderDetailBiz.getGuaranteeFee().compareTo(BigDecimal.ZERO)>0){
            feeTypeEnum = FeeTypeEnum.GUARANTEE_FEE;
        }else if(refundOrderDetailBiz.getConsultingFee().compareTo(BigDecimal.ZERO)>0){
            feeTypeEnum = FeeTypeEnum.CONSULTING_FEE;
        }else{
            throw new BizException("费用科目类型异常");
        }
        List<ProjectGuaranteeServicePlan> projectGuaranteeServicePlanList = projectGuaranteeServicePlanMapperExt.listNotReceiveByProjectIdAndFeeType(refundOrderDetailBiz.getProjectId(), feeTypeEnum.getValue());
        // 如垫付的费用不为空，还款人在还该费用时应该还给垫付出款方
        ProjectGuaranteeServicePlan targetProjectGuaranteeServicePlan = null;
        if(!projectGuaranteeServicePlanList.isEmpty()){
            for (ProjectGuaranteeServicePlan projectGuaranteeServicePlan : projectGuaranteeServicePlanList) {
                if(projectGuaranteeServicePlan.getCurrentPeriod().equals(refundOrderDetailBiz.getCurrentPeriod()) && projectGuaranteeServicePlan.getSourceUserId().equals(refundOrderDetailBiz.getTargetUserId())){
                    targetProjectGuaranteeServicePlan = projectGuaranteeServicePlan;
                    break;
                }
            }
        }
        if(targetProjectGuaranteeServicePlan == null){
            return;
        }

        logger.info("updateProjectGuaranteeServicePlan projectId:{}", refundOrderDetailBiz.getProjectId());

        ProjectGuaranteeServicePlan updateRecord = new ProjectGuaranteeServicePlan();
        updateRecord.setId(targetProjectGuaranteeServicePlan.getId());
        updateRecord.setReceivedAmount(targetProjectGuaranteeServicePlan.getReceivedAmount().add(refundOrderDetailBiz.getAmount()));

        if (updateRecord.getReceivedAmount().
                        compareTo(targetProjectGuaranteeServicePlan.getAmount()) >= 0) {
            updateRecord.setReceiveStatus((byte) 1);
        }

        projectGuaranteeServicePlanMapperExt.updateByPrimaryKeySelective(updateRecord);
    }

}
